package org.example.demo.controller;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.example.demo.cache.LoginUserCache;
import org.example.demo.common.GlobalSettings;
import org.example.demo.entity.dto.LoginUser;
import org.example.demo.entity.dto.Result;
import org.example.demo.entity.po.Operator;
import org.example.demo.service.OperatorService;
import org.example.demo.utils.JwtUtil;
import org.example.demo.utils.RequestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Controller
@Slf4j
@Api(tags = "首页模块")
public class IndexController {

    @Value("${app.jwt-secret-key}")
    private String jwtSecretKey;

    @Value("${app.user-secret-key}")
    private String userSecretKey;

    @Value("${app.user-token-expire-time}")
    private Integer userTokenExpireTime;

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private OperatorService operatorService;

    @Autowired
    private LoginUserCache loginUserCache;

    @GetMapping("/")
    @ApiOperation(value = "登录页")
    public String login(HttpServletRequest request, Model model) {
        String token = RequestUtil.getToken(request);
        JSONObject payloads = JwtUtil.getJwtPayloads(jwtSecretKey, token);
        if (payloads == null) {
            return "login";
        }
        String jwtId = (String) payloads.get(JWTPayload.JWT_ID);
        if (redisTemplate.hasKey(GlobalSettings.LOGIN_USER_BLACK_LIST_PREFIX + jwtId)) {
            return "login";
        }

        String username = (String) payloads.get(GlobalSettings.LOGIN_USER);
        LambdaQueryWrapper<Operator> wrapper = new LambdaQueryWrapper();
        wrapper.eq(Operator::getUsername, username);
        Operator operator = operatorService.getOne(wrapper);
        model.addAttribute("user", operator);
        return "index";
    }

    private String generateToken(String jwtId, String username) {
        Map<String, Object> payload = new HashMap<>();
        DateTime now = DateTime.now();
        DateTime expTime = now.offsetNew(DateField.MINUTE, userTokenExpireTime);
//        payload.put(JWTPayload.ISSUED_AT, now); // 签发时间
//        payload.put(JWTPayload.NOT_BEFORE, now);  // 生效时间
        payload.put(JWTPayload.EXPIRES_AT, expTime); // 过期时间
        payload.put(JWTPayload.JWT_ID, jwtId);
        payload.put(GlobalSettings.LOGIN_USER, username);
        String token = JWTUtil.createToken(payload, jwtSecretKey.getBytes());
        return token;
    }

    @PostMapping("/login")
    @ResponseBody
    @ApiOperation(value = "登录")
    public Result<String> login(@RequestBody Operator param, HttpServletResponse response) {
        LambdaQueryWrapper<Operator> wrapper = new LambdaQueryWrapper();
        wrapper.eq(Operator::getUsername, param.getUsername());
        Operator operator = operatorService.getOne(wrapper);

        if (operator == null) {
            log.error("登录失败：用户名或密码错误！");
            return Result.error("登录失败：用户名或密码错误！");
        }

        AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, userSecretKey.getBytes());
        String password = aes.encryptBase64(param.getPassword());
        if (password == null || !password.equals(operator.getPassword())) {
            log.error("登录失败：用户名或密码错误！");
            return Result.error("登录失败：用户名或密码错误！");
        }

        String jwtId = IdUtil.simpleUUID();
        String token = this.generateToken(jwtId, operator.getUsername());
        return Result.ok(token);
    }

    @GetMapping("/userinfo")
    @ResponseBody
    @ApiOperation(value = "获取登录用户信息")
    public Result<Operator> userinfo() {
        LoginUser loginUser = loginUserCache.get();
        LambdaQueryWrapper<Operator> wrapper = new LambdaQueryWrapper();
        wrapper.eq(Operator::getUsername, loginUser.getUsername());
        Operator operator = operatorService.getOne(wrapper);
        return Result.ok(operator);
    }

    @GetMapping("/renewal")
    @ResponseBody
    @ApiOperation(value = "续租")
    public Result<String> renewal(HttpServletRequest request) {
        String token = RequestUtil.getToken(request);
        JSONObject payloads = JwtUtil.getJwtPayloads(jwtSecretKey, token);
        String jwtId = (String) payloads.get(JWTPayload.JWT_ID);
        long expireTime = Long.parseLong(payloads.get(JWTPayload.EXPIRES_AT).toString()) * 1000;
        redisTemplate.opsForValue().set(GlobalSettings.LOGIN_USER_BLACK_LIST_PREFIX + jwtId, "", expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        String newJwtId = IdUtil.simpleUUID();
        String username = (String) payloads.get(GlobalSettings.LOGIN_USER);
        String newToken = this.generateToken(newJwtId, username);
        return Result.ok(newToken);
    }

    @GetMapping("/logout")
    @ResponseBody
    @ApiOperation(value = "登出")
    public Result<Boolean> logout(HttpServletRequest request) {
        String token = RequestUtil.getToken(request);
        JSONObject payloads = JwtUtil.getJwtPayloads(jwtSecretKey, token);
        String jwtId = (String) payloads.get(JWTPayload.JWT_ID);
        long expireTime = Long.parseLong(payloads.get(JWTPayload.EXPIRES_AT).toString()) * 1000;
        redisTemplate.opsForValue().set(GlobalSettings.LOGIN_USER_BLACK_LIST_PREFIX + jwtId, "", expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        return Result.ok(true);
    }
}
